package validator

import (
	"ecms/utils/reflects"
	"ecms/utils/response"
	"github.com/gin-gonic/gin"
	"strings"
)

var (
	TypeNumber         Type = &numberType{}
	TypeNumberNoneZero Type = &numberNoneZeroType{}
	TypeDecimal        Type = &decimalType{}
	TypePassword       Type = &passwordType{}
	TypeLink           Type = &linkType{}
	TypePhone          Type = &phoneType{}
	TypeEmail          Type = &emailType{}
	TypeNoEmpty        Type = &noEmptyType{}
	TypeAlphaDash      Type = &alphaDashType{}
	TypeStatus         Type = &statusType{}
	TypeAlpha          Type = &alphaType{}
)

type Rule struct {
	Field    string `json:"field"`
	Name     string `json:"name"`
	Required bool   `json:"required"`
	Type     Type   `json:"type"`
}

type Type interface {
	Validate(name string, val string, required bool) (bool, string)
}

func NewRule(field string, name string, required bool, checkType Type) *Rule {
	return &Rule{
		Field:    field,
		Name:     name,
		Required: required,
		Type:     checkType,
	}
}

func NewRules(rules ...*Rule) []*Rule {
	return rules
}

// ShouldBindFromQuery 从 *gin.Context 中验证提取 Query 并绑定到结构体指针 out 中
func ShouldBindFromQuery(ctx *gin.Context, out interface{}, rules ...*Rule) bool {

	refObj, err := reflects.NewObject(out)
	if err != nil {
		return false
	}

	errList := make([]response.ErrMsg, 0)
	simpleErrMsg := make([]string, 0)
	simpleSameErrMsgMap := make(map[string]bool)
	result := true
	for _, v := range rules {
		val := ctx.Query(v.Field)
		pass, msg := v.Type.Validate(v.Name, val, v.Required)
		if !pass {
			errList = append(errList, response.ErrMsg{
				Field: v.Field,
				Name:  v.Name,
				Msg:   msg,
			})
			if _, ok := simpleSameErrMsgMap[msg]; !ok {
				simpleErrMsg = append(simpleErrMsg, msg)
				simpleSameErrMsgMap[msg] = true
			}
			result = false
		} else {
			refObj.SetValueByString(v.Field, val)
		}
	}

	if !result {
		response.FailJson(ctx, strings.Join(simpleErrMsg, "\n"), errList)
		return false
	}

	return true
}

// ShouldBindFromPostForm 从 *gin.Context 中验证提取 PostForm 并绑定到结构体指针 out 中
func ShouldBindFromPostForm(ctx *gin.Context, out interface{}, rules ...*Rule) bool {

	refObj, err := reflects.NewObject(out)
	if err != nil {
		return false
	}

	errList := make([]response.ErrMsg, 0)
	simpleErrMsg := make([]string, 0)
	simpleSameErrMsgMap := make(map[string]bool)
	result := true
	for _, v := range rules {
		val := ctx.PostForm(v.Field)
		pass, msg := v.Type.Validate(v.Name, val, v.Required)
		if !pass {
			errList = append(errList, response.ErrMsg{
				Field: v.Field,
				Name:  v.Name,
				Msg:   msg,
			})
			if _, ok := simpleSameErrMsgMap[msg]; !ok {
				simpleErrMsg = append(simpleErrMsg, msg)
				simpleSameErrMsgMap[msg] = true
			}
			result = false
		} else {
			refObj.SetValueByString(v.Field, val)
		}
	}

	if !result {
		response.FailJson(ctx, strings.Join(simpleErrMsg, "\n"), errList)
		return false
	}

	return true
}

// ShouldBindMapFromPostForm 从 *gin.Context 中验证并提取 PostForm 到 reqData 中
func ShouldBindMapFromPostForm(ctx *gin.Context, reqData *map[string]string, rules ...*Rule) bool {
	errList := make([]response.ErrMsg, 0)
	simpleErrMsg := make([]string, 0)
	simpleSameErrMsgMap := make(map[string]bool)
	result := true
	for _, v := range rules {
		val := ctx.PostForm(v.Field)
		pass, msg := v.Type.Validate(v.Name, val, v.Required)
		if !pass {
			errList = append(errList, response.ErrMsg{
				Field: v.Field,
				Name:  v.Name,
				Msg:   msg,
			})
			if _, ok := simpleSameErrMsgMap[msg]; !ok {
				simpleErrMsg = append(simpleErrMsg, msg)
				simpleSameErrMsgMap[msg] = true
			}
			result = false
		} else {
			(*reqData)[v.Field] = val
		}
	}

	if !result {
		response.FailJson(ctx, strings.Join(simpleErrMsg, "\n"), errList)
		return false
	}

	return true
}

// ShouldBindMapFromQuery 从 *gin.Context 中验证并提取 Query 到 reqData 中
func ShouldBindMapFromQuery(ctx *gin.Context, reqData *map[string]string, rules ...*Rule) bool {
	errList := make([]response.ErrMsg, 0)
	simpleErrMsg := make([]string, 0)
	simpleSameErrMsgMap := make(map[string]bool)
	result := true
	for _, v := range rules {
		val := ctx.Query(v.Field)
		pass, msg := v.Type.Validate(v.Name, val, v.Required)
		if !pass {
			errList = append(errList, response.ErrMsg{
				Field: v.Field,
				Name:  v.Name,
				Msg:   msg,
			})
			if _, ok := simpleSameErrMsgMap[msg]; !ok {
				simpleErrMsg = append(simpleErrMsg, msg)
				simpleSameErrMsgMap[msg] = true
			}
			result = false
		} else {
			(*reqData)[v.Field] = val
		}
	}

	if !result {
		response.FailJson(ctx, strings.Join(simpleErrMsg, "\n"), errList)
		return false
	}

	return true
}
